package enumerable;

import java.util.Iterator;

@SuppressWarnings("serial")
public class HashMap<T, V> extends java.util.HashMap<T,V> implements Map<T,V> {

	@Override
	public <K> Map<T,V> select(Selector<java.util.Map.Entry<T, V>> select) {
		Map<T,V> map = new HashMap<T,V>();
		
	    Iterator<Entry<T,V>> it = this.entrySet().iterator();
	    while (it.hasNext()) {
	        Entry<T,V> elem = (Entry<T,V>)it.next();
	        if (select.predicate(elem)) {
	        	map.put(elem.getKey(), elem.getValue());
	        }
	    }
		
		return map;
	}

	@Override
	public <K> Enumerable<K> project(Projector<java.util.Map.Entry<T, V>, K> project) {
		List<K> list = new LinkedList<K>();
		
	    Iterator<Entry<T,V>> it = this.entrySet().iterator();
	    while (it.hasNext()) {
	        Entry<T,V> elem = (Entry<T,V>)it.next();
	        list.add(project.project(elem));
	    }
		
		return list;
	}

	@Override
	public java.util.Map.Entry<T, V> single() {
		Entry<T,V> elem = singleOrNull();
		if (elem == null) {
			throw new MultipleElementsException();
		}
		
		return elem;
	}

	@Override
	public java.util.Map.Entry<T, V> single(Selector<java.util.Map.Entry<T, V>> select) {
		return select(select).single();
	}

	@Override
	public java.util.Map.Entry<T, V> singleOrNull() {
		Entry<T,V> elem = null;
		
		if (this.size() == 1) {
			elem = this.first();
		}
		
		return elem;
	}

	@Override
	public java.util.Map.Entry<T, V> singleOrNull(Selector<java.util.Map.Entry<T, V>> select) {
		return this.select(select).singleOrNull();
	}

	@Override
	public java.util.Map.Entry<T, V> first() {
		Entry<T,V> elem = firstOrNull();
		if (elem == null) {
			throw new NoElementException();
		}
		
		return elem;
	}

	@Override
	public java.util.Map.Entry<T, V> first(Selector<java.util.Map.Entry<T, V>> select) {
		return select(select).first();
	}

	@Override
	public java.util.Map.Entry<T, V> firstOrNull() {
		Entry<T,V> elem = null;
		
		if (this.size() > 0) {
			elem = this.iterator().next();
		}
		
		return elem;
	}

	@Override
	public java.util.Map.Entry<T, V> firstOrNull(
			Selector<java.util.Map.Entry<T, V>> select) {
		return select(select).firstOrNull();
	}

	@Override
	public List<java.util.Map.Entry<T, V>> toList() {
		List<Entry<T,V>> list = new LinkedList<Entry<T,V>>();
		
		Iterator<Entry<T,V>> it = this.iterator();
		while (it.hasNext()) {
			list.add((Entry<T,V>)it.next());
		}
		
		return list;
	}

	@Override
	public boolean any(Selector<java.util.Map.Entry<T, V>> select) {
		boolean found = false;
		
		Iterator<Entry<T,V>> it = this.iterator();
		while (it.hasNext()) {
			Entry<T,V> elem = (Entry<T,V>)it.next();
			if (select.predicate(elem)) {
				found = true;
				break;
			}
		}
		
		return found;
	}

	@Override
	public boolean all(Selector<java.util.Map.Entry<T, V>> select) {
		return count(select) == this.size();
	}

	@Override
	public int count(Selector<java.util.Map.Entry<T, V>> select) {
		return select(select).count();
	}

	@Override
	public int count() {
		return this.size();
	}

	@Override
	public Iterator<java.util.Map.Entry<T, V>> iterator() {
		return this.entrySet().iterator();
	}
	
	@Override
	public <K> Enumerable<K> projectMany(Projector<java.util.Map.Entry<T, V>, Enumerable<K>> project) {
		List<K> list = new LinkedList<K>();
		
	    Iterator<Entry<T,V>> it = this.entrySet().iterator();
	    while (it.hasNext()) {
	        Entry<T,V> elem = (Entry<T,V>)it.next();
	        for (K subElem : project.project(elem)) {
	        	list.add(subElem);
	        }
	    }
		
		return list;
	}
	
	@Override
	public void forEach(Mapper<java.util.Map.Entry<T, V>> mapper) {
	    Iterator<Entry<T,V>> it = this.entrySet().iterator();
	    while (it.hasNext()) {
	        Entry<T,V> elem = (Entry<T,V>)it.next();
	        mapper.map(elem);
	    }
	}
}
